/**
 * Name:hack.js
 * Author:gene.jiang
 * E-mail:1013195469@qq.com
 * Website:http://www.jianggujin.com/
 * LICENSE:Apache v2 License
 */
layui.define(['tool'], function(exports) {
    var _tool_ = layui.tool;
    //修复字符串trim方法
    if(!_tool_.isNative('gene'.trim)) {
        var rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;
        String.prototype.trim = function() {
            return this.replace(rtrim, '');
        };
    };
    /**
     * 为字符串增加格式化字符串方法
     * @example 'hello,{0}'.format('jianggujin'); ==> 'hello,jianggujin'
     * @example 'hello,{name}'.format({name:'jianggujin'}); ==> 'hello,jianggujin'
     * @param {Array|Object} args
     * @return {String}
     */
    String.prototype.format = function(args) {
        var result = this;
        if(arguments.length > 0) {
            if(arguments.length == 1 && typeof(args) == "object") {
                result = result.replace(/\{(\w+)\}/g, function() {
                    var old = arguments[0],
                        key = arguments[1];
                    if(args[key] != undefined) {
                        return args[key];
                    }
                    return old;
                });
            } else {
                var arr = arguments;
                result = result.replace(/\{(\d+)\}/g, function() {
                    var old = arguments[0],
                        key = parseInt(arguments[1]);
                    if(arr[key] != undefined) {
                        return arr[key];
                    }
                    return old;
                });
            }
        }
        return result;
    };
    /**
     * 判断字符串是否以指定字符串开头
     * @param {String}
     * @return {Boolean}
     */
    String.prototype.startsWith = String.prototype.startsWith || function(str) {
        if(!str || str.length > this.length) {
            return false;
        }
        if(this.substr(0, str.length) == str) {
            return true;
        } else {
            return false;
        }
    };
    /**
     * 判断字符串是否以指定字符串结尾
     * @param {String}
     * @return {Boolean}
     */
    String.prototype.endsWith = String.prototype.endsWith || function(str) {
        if(!str || str.length > this.length) {
            return false;
        }
        if(this.substring(this.length - str.length) == str) {
            return true;
        } else {
            return false;
        }
    };

    //修复Object.create
    if(!Object.create) {
        Object.create = function() {
            function F() {}

            return function(o) {
                if(arguments.length != 1) {
                    throw new Error('Object.create implementation only accepts one parameter.');
                }
                F.prototype = o;
                return new F();
            };
        }();
    }
    //修复Object.keys方法
    var hasDontEnumBug = !{
        'toString': null
    }.propertyIsEnumerable('toString');
    var hasProtoEnumBug = function() {}.propertyIsEnumerable('prototype');
    var dontEnums = ['toString', 'toLocaleString', 'valueOf', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'constructor'];
    var dontEnumsLength = dontEnums.length;
    if(!_tool_.isNative(Object.keys)) {
        Object.keys = function(object) {
            //ecma262v5 15.2.3.14
            var theKeys = [];
            var skipProto = hasProtoEnumBug && typeof object === 'function';
            if(typeof object === 'string' || object && object.callee) {
                for(var i = 0; i < object.length; ++i) {
                    theKeys.push(String(i));
                }
            } else {
                for(var name in object) {
                    if(!(skipProto && name === 'prototype') && ohasOwn.call(object, name)) {
                        theKeys.push(String(name));
                    }
                }
            }

            if(hasDontEnumBug) {
                var ctor = object.constructor,
                    skipConstructor = ctor && ctor.prototype === object;
                for(var j = 0; j < dontEnumsLength; j++) {
                    var dontEnum = dontEnums[j];
                    if(!(skipConstructor && dontEnum === 'constructor') && ohasOwn.call(object, dontEnum)) {
                        theKeys.push(dontEnum);
                    }
                }
            }
            return theKeys;
        };
    }

    //修复Array.isArray方法
    if(!_tool_.isNative(Array.isArray)) {
        Array.isArray = function(a) {
            return Object.prototype.toString.call(a) === '[object Array]';
        };
    }

    function iterator(vars, body, ret) {
        var fun = 'for(var ' + vars + 'i=0,n = this.length; i < n; i++){' + body.replace('_', '((i in this) && fn.call(scope,this[i],i,this))') + '}' + ret;
        return Function('fn,scope', fun);
    }

    if(!_tool_.isNative(Array.prototype.map)) {
        _tool_.shadowCopy(Array.prototype, {
            /**
             * 定位操作，返回数组中第一个等于给定参数的元素的索引值。
             * @param {Object} item
             * @param {Number} index
             */
            indexOf: function(item, index) {
                var n = this.length,
                    i = ~~index;
                if(i < 0) i += n;
                for(; i < n; i++) {
                    if(this[i] === item) return i;
                }
                return -1;
            },
            /**
             * 定位操作，同上，不过是从后遍历。
             * @param {Object} item
             * @param {Number} index
             */
            lastIndexOf: function(item, index) {
                var n = this.length,
                    i = index == null ? n - 1 : index;
                if(i < 0) i = Math.max(0, n + i);
                for(; i >= 0; i--) {
                    if(this[i] === item) return i;
                }
                return -1;
            },
            /**
             * 迭代操作，将数组的元素挨个儿传入一个函数中执行。Prototype.js的对应名字为each。
             */
            forEach: iterator('', '_', ''),
            /**
             * 迭代类 在数组中的每个项上运行一个函数，如果此函数的值为真，则此元素作为新数组的元素收集起来，并返回新数组
             */
            filter: iterator('r=[],j=0,', 'if(_)r[j++]=this[i]', 'return r'),
            /**
             * 收集操作，将数组的元素挨个儿传入一个函数中执行，然后把它们的返回值组成一个新数组返回。Prototype.js的对应名字为collect。
             */
            map: iterator('r=[],', 'r[i]=_', 'return r'),
            /**
             * 只要数组中有一个元素满足条件（放进给定函数返回true），那么它就返回true。Prototype.js的对应名字为any。
             */
            some: iterator('', 'if(_)return true', 'return false'),
            /**
             * 只有数组中的元素都满足条件（放进给定函数返回true），它才返回true。Prototype.js的对应名字为all。
             */
            every: iterator('', 'if(!_)return false', 'return true')
        });
    }
    _tool_.shadowCopy(Array.prototype, {
        /**
         * 合并数组
         * @param {Array} other
         * @return {Array}
         */
        merge: function(other) {
            this.push.apply(this, other);
            return this;
        },
        /**
         * 确保数组中不存在元素时添加
         * @param {Object} item
         * @return {Number}
         */
        ensure: function(item) {
            //只有当前数组不存在此元素时只添加它
            if(this.indexOf(item) === -1) {
                return this.push(item);
            }
            return this.length;
        },
        /**
         * 移除数组指定索引处元素，返回布尔表示成功与否
         * @param {Number} index
         * @return {Boolean}
         */
        removeAt: function(index) {
            //移除数组中指定位置的元素，返回布尔表示成功与否
            return !!this.splice(index, 1).length;
        },
        /**
         * 移除数组中第一个匹配传参的那个元素，返回布尔表示成功与否
         * @param {Array} target
         * @param {Object} item
         * @return {Boolean}
         */
        remove: function(item) {
            var index = this.indexOf(item);
            if(~index) {
                return this.removeAt(index);
            }
            return false;
        }
    });

    //解决IE10以下不支持Function.bind
    if(!_tool_.isNative(_tool_.isNative.bind)) {
        /* istanbul ignore next*/
        Function.prototype.bind = function(scope) {
            if(arguments.length < 2 && scope === void 0) return this;
            var fn = this,
                argv = arguments;
            return function() {
                var args = [],
                    i;
                for(i = 1; i < argv.length; i++) {
                    args.push(argv[i]);
                }
                for(i = 0; i < arguments.length; i++) {
                    args.push(arguments[i]);
                }
                return fn.apply(scope, args);
            };
        };
    }
    exports('hack');
});